/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!				\file		CC_PlugIn.cc
 \date		june 2010
 \author		TNick

 \brief		Implements CC_PlugIn class


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read ReadMe.txt and Licence.txt in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	"../main.h"

#include	<assert.h>
#include	<QFileInfo>

#include	"CC_Err.h"
#include	"CC_App.h"
#include	"CC_PlgInTpl.h"

/*  INCLUDES    ============================================================ */
//
//
//
//
/*  DEFINITIONS    --------------------------------------------------------- */


/*  DEFINITIONS    ========================================================= */
//
//
//
//
/*  DATA    ---------------------------------------------------------------- */

unsigned		CC_PlugIn::u_id_base;


/*  DATA    ================================================================ */
//
//
//
//
/*  FUNCTIONS    ----------------------------------------------------------- */












//Q_UNUSED(p_err);

//unsigned			id_to_use;
//QPluginLoader *		qtplg = NULL;
//QObject *			obj_plgin = NULL;
//CC_PlgInTpl *		tpl_plgin = NULL;

//CC_PlugIn *			internplgin = NULL;


///* check if this plug-in was previously loaded */
//internplgin = p_hd_plugs->itemFromName(s_base_name);

//if (internplgin == NULL)
//	{


//		/* and create a class instance for it */
//		internplgin = new CC_PlugIn(qtplg, tpl_plgin, s_file, id_to_use);
//		if (internplgin == NULL)
//			{
//				p_err->Add(CCERR::EERR,
//						   CCERR::CTX_PLUGINS,
//						   QObject::tr("Plug-in %1 could not be loaded due to memory allocation failure")
//						   .arg(s_file));
//				b_ret = CCERR::ERR_MALLOC;
//				goto Failed_exit;
//			}
//	}
//else
//	{ /// if (internplgin != NULL)
//		/*
// A module with this name already exists
// */
//		if (internplgin->isShadow())
//			{ /* this is a structure that may be reused */


//			}
//		else
//			{ /* this is an error */
//				p_err->Add(CCERR::EERR,
//						   CCERR::CTX_PLUGINS,
//						   QObject::tr("Plug-in %1 already loaded from: %2")
//						   .arg(internplgin->Name())
//						   .arg(s_file));
//				b_ret = CCERR::ERR_MALLOC;
//				goto Failed_exit;
//			}
//	}












/* ------------------------------------------------------------------------- */
/* constructor	                                                             */
CC_PlugIn::CC_PlugIn		(QString s_name, QString s_file, CC_Err * p_err)
	: CC_L2DN(s_name)
{
	CC_HDL2DN *			p_hd_plugs = CC_App::pluginHeader();
	interf = NULL;

	/* get an associated id */
	u_id = getFreeID();
	if (u_id == CORE_MODULE_ID)
		{
			p_err->Add(CCERR::EERR,
					   CCERR::CTX_PLUGINS,
					   QObject::tr("Maximum number of plugins (%1) has been reached")
					   .arg(MAX_PLUGINS));
			goto Failed_exit;
		}

	/* common routine for init and reinit */
	if (!SUCCEDED(reinit(s_file,p_err)))
		goto Failed_exit;

	/* acknoledge */
	p_hd_plugs->append(this);

Fnc_Exit:
	return;
Failed_exit:
	u_id = CORE_MODULE_ID;
	qt_plg.unload();
	goto Fnc_Exit;
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
/* starts up the plug-in system and loads plug-ins from predefined folder    */
CCERR::EERRORS		CC_PlugIn::startPlugInSystem	(CC_Err*p_err)
{
	Q_UNUSED(p_err);
	CCERR::EERRORS		b_ret = CCERR::ERR_SUCCESS;

	/* base number for plug-in identification */
	u_id_base = 1;

	/* get plug-in folder inside application folder */
	QDir pluginsDir = QDir(qApp->applicationDirPath());

#if defined(Q_OS_WIN)
	if (pluginsDir.dirName().toLower() == "debug" ||
			pluginsDir.dirName().toLower() == "release")
		pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
	if (pluginsDir.dirName() == "MacOS")
		{
			pluginsDir.cdUp();
			pluginsDir.cdUp();
			pluginsDir.cdUp();
		}
#endif
	for  (;;)
		{
			qDebug() << "Checking path " << pluginsDir.path();
			if (!pluginsDir.cd("plugins"))
				{
					qDebug() << "does not exist";
					pluginsDir.cdUp();
					qDebug() << "Checking path " << pluginsDir.path();
					if (!pluginsDir.cd("plugins"))
						{
							p_err->Add(CCERR::EDEBUG,
									   CCERR::CTX_PLUGINS,
									   QObject::tr("Plug-In folder could not be located"));
							break;
						}
				}
			qDebug() << "Using path " << pluginsDir.path();

			/* iterate the files and attempt to load */
			foreach (QString fileName,
					 pluginsDir.entryList(QDir::Files))
				{
					QString s_fullp = pluginsDir.absoluteFilePath(fileName);
					if (QLibrary::isLibrary(s_fullp))
						{
							if (!SUCCEDED(
										load(
											s_fullp,
											NULL,
											p_err
											)
										)
									)
								{
									p_err->Add(CCERR::EDEBUG,
											   CCERR::CTX_PLUGINS,
											   QObject::tr("Start-up plug-in %1 could not be loaded").arg(fileName));
								}
						}
				}
			break;
		}

	return b_ret;

}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
CCERR::EERRORS		CC_PlugIn::refreshPlugInSystem	(unsigned * p_new,
													 CC_Err * p_err)
{
	Q_UNUSED(p_err);
	Q_UNUSED(p_new);

	return CCERR::ERR_SUCCESS;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
CCERR::EERRORS		CC_PlugIn::terminatePlugInSystem	(CC_Err * p_err)
{
	Q_UNUSED(p_err);
	CC_HDL2DN *			p_hd_plugs = CC_App::pluginHeader();

	CC_PlugIn *	pI = static_cast<CC_PlugIn *>(p_hd_plugs->first());
	while (pI != NULL)
		{
			pI->terminate(p_err);
			CC_PlugIn*	pI2 = static_cast<CC_PlugIn *>(pI->next());
			pI->extract(p_hd_plugs);
			delete pI;
			pI = pI2;
		}

	return CCERR::ERR_SUCCESS;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
CCERR::EERRORS		CC_PlugIn::reinit		(QString s_file, CC_Err * p_err)
{
	QObject *			obj_plgin = NULL;
	CCERR::EERRORS		b_ret = CCERR::ERR_SUCCESS;
	s_full_path = s_file;

	/* use QT way of loading */
	qt_plg.setFileName(s_file);
	obj_plgin = qt_plg.instance();
	if (obj_plgin == NULL)
		{
			p_err->Add(CCERR::EERR,
					   CCERR::CTX_PLUGINS,
					   QObject::tr("Plug-in %1 could not be loaded. Error: \n%2\n")
					   .arg(s_file)
					   .arg(qt_plg.errorString()));
			b_ret = CCERR::ERR_USER;
			goto Failed_exit;
		}
	interf = qobject_cast<CC_PlgInTpl *>(obj_plgin);

	last_load.start();

	/* see if the plug-in is able to initialize itself */
	if (!interf->init())
		{
			p_err->Add(CCERR::EERR,
					   CCERR::CTX_PLUGINS,
					   QObject::tr("Plug-in %1 could not be loaded. "
								   "Initialization function failed.")
					   .arg(Name()));
			b_ret = CCERR::ERR_USER;
			goto Failed_exit;
		}

Fnc_Exit:
	return b_ret;
Failed_exit:
	qt_plg.unload();
	goto Fnc_Exit;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
CCERR::EERRORS		CC_PlugIn::load			(QString s_file,
											 CC_PlugIn** p_plg, CC_Err* p_err)
{

	QString				s_base_name;
	CC_PlugIn *			internplgin;
	CCERR::EERRORS		b_ret = CCERR::ERR_SUCCESS;
	CC_HDL2DN *			p_hd_plugs = CC_App::pluginHeader();

	/* check the string for consistency and get a name */
	s_base_name = getNameFromPath(s_file);
	if (s_base_name.isEmpty())
		{
			p_err->Add(CCERR::EERR,
					   CCERR::CTX_PLUGINS,
					   QObject::tr("An invalid name has been provided for plug-in loader."));
			b_ret = CCERR::ERR_USER;
			goto Failed_exit;
		}

	/* check if this plug-in was previously loaded */
	internplgin = static_cast<CC_PlugIn*>(p_hd_plugs->itemFromName(s_base_name));

	if (internplgin == NULL)
		{ /* this is the first time we see this item */

			/* so create a new host structure */
			internplgin = new CC_PlugIn(s_base_name,s_file,p_err);
			if (!internplgin->isOK())
				{
					p_err->Add(CCERR::EERR,
							   CCERR::CTX_PLUGINS,
							   QObject::tr("Can't load %1 plug-in.").arg(s_file));
					b_ret = CCERR::ERR_USER;
					goto Failed_exit;
				}
		}
	else
		{ /* an item with a name like this already exists */

			/* so see if it's empty */
			if (internplgin->isShadow())
				{
					internplgin->reinit(s_file,p_err);
				}
			else
				{ /* this is an error */
					p_err->Add(CCERR::EERR,
							   CCERR::CTX_PLUGINS,
							   QObject::tr("Plug-in %1 already loaded from: %2")
							   .arg(internplgin->Name())
							   .arg(internplgin->path()));
					b_ret = CCERR::ERR_MALLOC;
					goto Failed_exit;
				}
		}

	/*
 beyond this point the plug-in is fully
 functional from our perspective; any errors are followed by a
 standard plug-in unload
  */

	if (p_plg != NULL)
		*p_plg = internplgin;



Fnc_Exit:
	return b_ret;
Failed_exit:
	if (internplgin != NULL)
		{
			internplgin->terminate(p_err);
		}
	goto Fnc_Exit;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
/// remove a plug-in from loaded list
void			CC_PlugIn::terminate			(CC_Err * p_err)
{
	Q_UNUSED(p_err);

	/* tell him about it */
	if (interf != NULL)
		interf->end();

	/* inform qt about the decision */
	qt_plg.unload();

	/* interface will no longer be available */
	interf = NULL;

	/* there is no full path */
	s_full_path = QString();

	/* log this */
	last_unload.start();

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
int				CC_PlugIn::index				(void) const
{
	return CC_L2DN::index(CC_App::pluginHeader());
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
unsigned		CC_PlugIn::getFreeID			(void)
{
	unsigned	i = u_id_base;
	bool		b_match;
	CC_HDL2DN*	hd_p = CC_App::pluginHeader();
	if (hd_p->count() >= MAX_PLUGINS)
		return CORE_MODULE_ID;

	for (;;)
		{
			CC_PlugIn*	it = static_cast<CC_PlugIn*>(hd_p->first());
			b_match = false;
			while (it != NULL)
				{
					if (i == it->u_id)
						{
							i++;
							b_match = true;
							break;
						}
					it = static_cast<CC_PlugIn*>(it->next());
				}
			if (b_match == false)
				{
					u_id_base = i+1;
					return i;
				}
			if (i == CORE_MODULE_ID)
				i++;
			if (i > MAX_PLUGINS)
				i = 0;
		}
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QString			CC_PlugIn::getNameFromPath		(QString s_file)
{
	if (s_file.isEmpty())
		{
			return QString();
		}

	QFileInfo finf(s_file);
	if (finf.exists() == false)
		{
			return QString();
		}
	QString fl_name = finf.baseName();
	QString fl_extr = QString::fromAscii("PlugIn_");
	if (fl_name.startsWith(fl_extr))
		{
			fl_name = fl_name.mid(finf.size());
		}
	return fl_name;
}
/* ========================================================================= */

/*  FUNCTIONS    =========================================================== */
//
//
//
//
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
